/**************************************************************************************
 *  Copyright (c)  2010  Leszek Karcz, Changmin Bae.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.3
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License". 
 **************************************************************************************/
package data;
import data.tileables.*;

/**************************************************************************************
 * Author(s): Leszek Karcz & Kyle VanWie, Changmin Bae
 * this class handles all data and methods specific to a player
 **************************************************************************************/
public class Player
{
	public static final int FOOD = 0, METAL = 1, ENERGY = 2; 
	private GameplayPlayerData gameplayPlayerData;
	private int ID;
	private int startX;
	private int startY;
	private int food;
	private int metal;
	private int energy;
	private int workers;
	private AreaBonusHash areaBonusHash=new AreaBonusHash();
	private ourResearchTree researchTree=new ourResearchTree();
	private CombatManager combatManager;
	
	//structure ID tables
	private IDTable structureIDs=new IDTable(10);
	
	private IDTable baseIDs=new IDTable(10);
	private IDTable farmIDs=new IDTable(10);
	private IDTable fortIDs=new IDTable(10);
	private IDTable labIDs=new IDTable(10);
	private IDTable plantIDs=new IDTable(10);
	private IDTable towerIDs=new IDTable(10);
	private IDTable smelterIDs=new IDTable(10);
	
	//unit ID tables
	private IDTable unitIDs=new IDTable(25);
	
	private IDTable colonyShipIDs=new IDTable(10);
	private IDTable explorerIDs=new IDTable(10);
	private IDTable meleeIDs=new IDTable(10);
	private IDTable rangeIDs=new IDTable(10);

	private IDTable rallyIDs=new IDTable(10);
	
	private SelectorList<Object> modes = new SelectorList<Object>();
	//Unit lists
		private LinkedList<Object> units = new LinkedList<Object>();
			private LinkedList<Explorer> explorers = new LinkedList<Explorer>();
			private LinkedList<ColonyShip> colonyShips = new LinkedList<ColonyShip>();
			private LinkedList<Melee> melees = new LinkedList<Melee>();
			private LinkedList<Range> rangeds = new LinkedList<Range>();
	//Structure lists
		private LinkedList<Object> structures = new LinkedList<Object>();
			private LinkedList<Base> bases = new LinkedList<Base>(); 
			private LinkedList<Farm> farms = new LinkedList<Farm>();
			private LinkedList<Fort> forts = new LinkedList<Fort>();
			private LinkedList<Laboratory> labs = new LinkedList<Laboratory>();
			private LinkedList<PowerPlant> plants = new LinkedList<PowerPlant>();
			private LinkedList<Smelter>	smelters = new LinkedList<Smelter>();
			private LinkedList<WatchTower> watchTowers = new LinkedList<WatchTower>();
	
	//Rally lists
		private LinkedList<Object> rallies = new LinkedList<Object>();			
			private LinkedList<Rally> rally = new LinkedList<Rally>();
			private LinkedList<Unit> combatArmy = new LinkedList<Unit>();
			private LinkedList<Unit> supportArmy = new LinkedList<Unit>();
			
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * Player constructor
	 **************************************************************************************/
	public Player(int ID, int startX, int startY, CombatManager combatMngr)
	{
		this.ID=ID;
		this.startX=startX;
		this.startY=startY;
		this.food=50000;
		this.metal=50000;
		this.energy=50000;
		this.workers=0;
		this.combatManager=combatMngr;

		units.addBack(explorers);
		units.addBack(colonyShips);
		units.addBack(melees);
		units.addBack(rangeds);
		
		structures.addBack(bases);
		structures.addBack(farms);
		structures.addBack(forts);
		structures.addBack(labs);
		structures.addBack(plants);
		structures.addBack(smelters);
		structures.addBack(watchTowers);
		
		rallies.addBack(rally);
		rallies.addBack(combatArmy);
		rallies.addBack(supportArmy);
		
		modes.addBack(units);
		modes.addBack(structures);
		modes.addBack(rallies);
		//modes.setSelected();
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * links GameplayPlayerData to the Player
	 **************************************************************************************/
	public void setGameplayData(GameplayPlayerData gameplayPlayerData)
	{
		this.gameplayPlayerData=gameplayPlayerData;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz, Changmin Bae
	 * executes players per tick instructions
	 **************************************************************************************/
	public void execute()
	{
		//execution to be handled in one for loop when the list of lists structure will be implemented
		
		
		
		//order all rallies to execute
		if(this.rally.getSize()>0)
		{
			this.rally.setCurrent(0);
			for(int i=0; i<this.rally.getSize();i++)
			{
				this.rally.peekIndex(i).execute();
			}
		}
		
		//order all bases to execute
		if(this.bases.getSize()>0)
		{
			this.bases.setCurrent(0);
			for(int i=0; i<this.bases.getSize();i++)
			{
				this.bases.peekIndex(i).execute();
			}
		}
		
		//order all farms to execute
		if(this.farms.getSize()>0)
		{
			this.farms.setCurrent(0);
			for(int i=0; i<this.farms.getSize();i++)
			{
				this.farms.peekIndex(i).execute();
			}
		}
		
		//order all labs to execute
		if(this.labs.getSize()>0)
		{
			this.labs.setCurrent(0);
			for(int i=0; i<this.labs.getSize();i++)
			{
				this.labs.peekIndex(i).execute();
			}
		}
		
		//order all watch towers to execute
		if(this.watchTowers.getSize()>0)
		{
			this.watchTowers.setCurrent(0);
			for(int i=0; i<this.watchTowers.getSize();i++)
			{
				this.watchTowers.peekIndex(i).execute();
			}
		}
		
		//order all forts to execute
		if(this.forts.getSize()>0)
		{
			this.forts.setCurrent(0);
			for(int i=0; i<this.forts.getSize();i++)
			{
				this.forts.peekIndex(i).execute();
			}
		}
		
		//order all plants to execute
		if(this.plants.getSize()>0)
		{
			this.plants.setCurrent(0);
			for(int i=0; i<this.plants.getSize();i++)
			{
				this.plants.peekIndex(i).execute();
			}
		}
		
		//order all smelters to execute
		if(this.smelters.getSize()>0)
		{
			this.smelters.setCurrent(0);
			for(int i=0; i<this.smelters.getSize();i++)
			{
				this.smelters.peekIndex(i).execute();
			}
		}
		
		//order all explorers to execute
		if(this.explorers.getSize()>0)
		{
			this.explorers.setCurrent(0);
			for(int i=0; i<this.explorers.getSize();i++)
			{
				this.explorers.peekIndex(i).execute();
			}
		}
		
		//order all colony ships to execute
		if(this.colonyShips.getSize()>0)
		{
			this.colonyShips.setCurrent(0);
			for(int i=0; i<this.colonyShips.getSize();i++)
			{
				this.colonyShips.peekIndex(i).execute();
			}
		}
		
		//order all melees to execute
		if(this.melees.getSize()>0)
		{
			this.melees.setCurrent(0);
			for(int i=0; i<this.melees.getSize();i++)
			{
				this.melees.peekIndex(i).execute();
			}
		}
		
		//order all rangeds to execute
		if(this.rangeds.getSize()>0)
		{
			this.rangeds.setCurrent(0);
			for(int i=0; i<this.rangeds.getSize();i++)
			{
				this.rangeds.peekIndex(i).execute();
			}
		}
		
		//update view for all bases
		if(this.bases.getSize()>0)
		{
		this.bases.setCurrent(0);
			for(int i=0; i<this.bases.getSize();i++)
			{
				int x=this.bases.peekIndex(i).getX();
				int y=this.bases.peekIndex(i).getY();
				int visionRadius=this.bases.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		//update view for all watch towers
		if(this.watchTowers.getSize()>0)
		{
		this.watchTowers.setCurrent(0);
			for(int i=0; i<this.watchTowers.getSize();i++)
			{
				int x=this.watchTowers.peekIndex(i).getX();
				int y=this.watchTowers.peekIndex(i).getY();
				int visionRadius=this.watchTowers.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		//update view for all farms
		if(this.farms.getSize()>0)
		{
		this.farms.setCurrent(0);
			for(int i=0; i<this.farms.getSize();i++)
			{
				int x=this.farms.peekIndex(i).getX();
				int y=this.farms.peekIndex(i).getY();
				int visionRadius=this.farms.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		//update view for all labs
		if(this.labs.getSize()>0)
		{
		this.labs.setCurrent(0);
			for(int i=0; i<this.labs.getSize();i++)
			{
				int x=this.labs.peekIndex(i).getX();
				int y=this.labs.peekIndex(i).getY();
				int visionRadius=this.labs.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all forts
		if(this.forts.getSize()>0)
		{
		this.forts.setCurrent(0);
			for(int i=0; i<this.forts.getSize();i++)
			{
				int x=this.forts.peekIndex(i).getX();
				int y=this.forts.peekIndex(i).getY();
				int visionRadius=this.forts.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all plants
		if(this.plants.getSize()>0)
		{
		this.plants.setCurrent(0);
			for(int i=0; i<this.plants.getSize();i++)
			{
				int x=this.plants.peekIndex(i).getX();
				int y=this.plants.peekIndex(i).getY();
				int visionRadius=this.plants.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all smelters
		if(this.smelters.getSize()>0)
		{
		this.smelters.setCurrent(0);
			for(int i=0; i<this.smelters.getSize();i++)
			{
				int x=this.smelters.peekIndex(i).getX();
				int y=this.smelters.peekIndex(i).getY();
				int visionRadius=this.smelters.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all explorers
		if(this.explorers.getSize()>0)
		{
		this.explorers.setCurrent(0);
			for(int i=0; i<this.explorers.getSize();i++)
			{
				int x=this.explorers.peekIndex(i).getX();
				int y=this.explorers.peekIndex(i).getY();
				int visionRadius=this.explorers.peekIndex(i).getVisionRadius();
				boolean prospecting = this.explorers.peekIndex(i).isProspecting();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, prospecting);
			}
		}
		
		//update view for all colony ships
		if(this.colonyShips.getSize()>0)
		{
		this.colonyShips.setCurrent(0);
			for(int i=0; i<this.colonyShips.getSize();i++)
			{
				int x=this.colonyShips.peekIndex(i).getX();
				int y=this.colonyShips.peekIndex(i).getY();
				int visionRadius=this.colonyShips.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all melees
		if(this.melees.getSize()>0)
		{
		this.melees.setCurrent(0);
			for(int i=0; i<this.melees.getSize();i++)
			{
				int x=this.melees.peekIndex(i).getX();
				int y=this.melees.peekIndex(i).getY();
				int visionRadius=this.melees.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		//update view for all rangeds
		if(this.rangeds.getSize()>0)
		{
		this.rangeds.setCurrent(0);
			for(int i=0; i<this.rangeds.getSize();i++)
			{
				int x=this.rangeds.peekIndex(i).getX();
				int y=this.rangeds.peekIndex(i).getY();
				int visionRadius=this.rangeds.peekIndex(i).getVisionRadius();
				this.gameplayPlayerData.refreshArea(x, y, visionRadius, false);
			}
		}
		
		
		for(int i = 0; i < farms.getSize(); i++){
			int[] resources = farms.peekIndex(i).harvest();
			
			metal += resources[METAL];
			food += resources[FOOD];
			energy += resources[ENERGY];
		}
		
		for(int i = 0; i < bases.getSize(); i++){
			int[] resources =  bases.peekIndex(i).harvest();
			
			metal += resources[METAL];
			food += resources[FOOD];
			energy += resources[ENERGY];
		}
		
		for(int i = 0; i < plants.getSize(); i++){
			int[] resources =  plants.peekIndex(i).harvest();
			
			metal += resources[METAL];
			food += resources[FOOD];
			energy += resources[ENERGY];
		}
		
		for(int i = 0; i < smelters.getSize(); i++){
			int[] resources =  smelters.peekIndex(i).harvest();
			
			metal += resources[METAL];
			food += resources[FOOD];
			energy += resources[ENERGY];
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns player ID
	 **************************************************************************************/
	public int getID()
	{
		return this.ID;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns players start position
	 **************************************************************************************/
	public int[] getStartPosition()
	{
		return new int[]{this.startX,this.startY};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns amount for each of resources player has
	 **************************************************************************************/
	public int[] getResources()
	{
		return new int[]{this.food,this.metal,this.energy};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns number of workers player owns
	 **************************************************************************************/
	public int getWorkerCount()
	{
		return this.workers;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * change the number of workers player owns
	 **************************************************************************************/
	public void changeWorkerCount(int value)
	{
		this.workers+=value;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new explorer
	 **************************************************************************************/
	public boolean createExplorer()
	{
		return this.createExplorer(this.startX, this.startY);
	}
	
	
	public boolean createExplorer(int x, int y)
	{
		
		if(this.gameplayPlayerData.verifyCursor(x, y) && this.gameplayPlayerData.tileIsPassable(x, y)) //validate if [x,y] is within bounds
		{
			int newUnitID=this.unitIDs.getFirstFree(); //get newUnit ID			
			if(newUnitID!=-1)
			{
			int newID=this.explorerIDs.getFirstFree(); //get new ID
				if(newID!=-1)
				{
					//create a new explorer
					Explorer explorer = new Explorer(newID, newUnitID, this);
					//add the explorer to explorers list
					this.explorers.addIndex(explorer, newID);
					//put the explorer on the map
					this.gameplayPlayerData.putControllableOnMap(x,y,explorer,true);
					if(modes.selected == null){
						modes.setSelected();
					}
					return true;
				}
				else
				{
					this.unitIDs.release(newUnitID); //release unitID
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new colony ship
	 **************************************************************************************/
	public boolean createColonyShip()
	{
		return this.createColonyShip(this.startX, this.startY);
	}
	public boolean createColonyShip(int x, int y)
	{
		return this.createColonyShip(x, y, 5);
	}
	public boolean createColonyShip(int x, int y, int workers)
	{
		if(this.gameplayPlayerData.verifyCursor(x, y) && this.gameplayPlayerData.tileIsPassable(x, y)) //validate if [x,y] is within bounds
		{			
			int newUnitID=this.unitIDs.getFirstFree(); //get newUnit ID			
			if(newUnitID!=-1)
			{			
			int newID=this.colonyShipIDs.getFirstFree(); //get new ID
				if(newID!=-1)
				{
					//create a new colonyShip
					ColonyShip colonyShip = new ColonyShip(newID, newUnitID, this, workers);
					//add the colonyShip to colonyShip list
					this.colonyShips.addIndex(colonyShip, newID);
					//put the colonyShip on the map
					this.gameplayPlayerData.putControllableOnMap(x,y,colonyShip,true);
					if(modes.selected == null){
						modes.setSelected();
					}
					return true;
				}
				else
				{
					this.unitIDs.release(newUnitID); //release unitID
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new melee
	 **************************************************************************************/
	public boolean createMelee()
	{
		return this.createMelee(this.startX, this.startY);
	}
	public boolean createMelee(int x, int y)
	{
		
		if(this.gameplayPlayerData.verifyCursor(x, y) && this.gameplayPlayerData.tileIsPassable(x, y)) //validate if [x,y] is within bounds
		{
			int newUnitID=this.unitIDs.getFirstFree(); //get newUnit ID		
			if(newUnitID!=-1)
			{	
				int newID=this.meleeIDs.getFirstFree(); //get new ID
				if(newID!=-1)
				{
					//create a new melee
					Melee melee = new Melee(newID, newUnitID, this);
					//add the melee to melee list
					this.melees.addIndex(melee, newID);
					//put the melee on the map
					this.gameplayPlayerData.putControllableOnMap(x,y,melee,true);
					if(modes.selected == null){
						modes.setSelected();
					}
					
					return true;
				}
				else
				{
					this.unitIDs.release(newUnitID); //release unitID
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new range
	 **************************************************************************************/
	public boolean createRange()
	{
		return this.createRange(this.startX, this.startY);
	}
	public boolean createRange(int x, int y)
	{
		
		if(this.gameplayPlayerData.verifyCursor(x, y) && this.gameplayPlayerData.tileIsPassable(x, y)) //validate if [x,y] is within bounds
		{
			int newUnitID=this.unitIDs.getFirstFree(); //get newUnit ID			
			if(newUnitID!=-1)
			{	
				int newID=this.rangeIDs.getFirstFree(); //get new ID
				if(newID!=-1)
				{
					//create a new range
					Range range = new Range(newID, newUnitID, this);
					//add the range to range list
					this.rangeds.addIndex(range, newID);
					//put the range on the map
					this.gameplayPlayerData.putControllableOnMap(x,y,range,true);
					if(modes.selected == null){
						modes.setSelected();
					}
					
					return true;
				}
				else
				{
					this.unitIDs.release(newUnitID); //release unitID
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * Creates a new rally
	 **************************************************************************************/
	public boolean createRally()
	{
		return this.createRally(this.startX, this.startY);
	}
	public boolean createRally(int x, int y)
	{
		if(this.gameplayPlayerData.verifyCursor(x, y) && this.gameplayPlayerData.tileIsPassable(x, y)) //validate if [x,y] is within bounds
		{
			int newID=this.rallyIDs.getFirstFree(); //get new ID
			if(newID!=-1)
			{
				//create a new tower
				Rally rally = new Rally(newID, this);
				//add the rally to rally list
				this.rally.addIndex(rally, newID);
				//put the rally on the map
				this.gameplayPlayerData.putControllableOnMap(x,y,rally,false);
				
				
				if(modes.selected == null){
					modes.setSelected();
				}
				return true;
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new base
	 **************************************************************************************/
	public boolean createBase(int workers)
	{
		return this.createBase(this.startX, this.startY, workers);
	}
	public boolean createBase(int x, int y,int workers)
	{		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID	
				if(newStructureID!=-1)
				{	
					int newID=this.baseIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new base
						Base base = new Base(newID, newStructureID, this, workers);
						//add the base to Base list
						this.bases.addIndex(base, newID);
						//put the base on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,base,true);	
						if(modes.selected == null){
							modes.setSelected();
						}
						return true;
					}
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new farm
	 **************************************************************************************/
	public boolean createFarm(int workers)
	{
		return this.createFarm(this.startX, this.startY, workers);
	}
	public boolean createFarm(int x, int y, int workers)
	{
		if(Farm.PRICE[FOOD] > food || Farm.PRICE[METAL] > metal || Farm.PRICE[ENERGY] > energy)
			return false;
		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{	
					int newID=this.farmIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new farm
						Farm farm = new Farm(newID, newStructureID, this, workers);
						//add the farm to Farm list
						this.farms.addIndex(farm, newID);
						//put the farm on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,farm,true);
						
						if(modes.selected == null){
							modes.setSelected();
						}
				
						food -= Farm.PRICE[FOOD];
						metal -= Farm.PRICE[METAL];
						energy -=Farm.PRICE[ENERGY];
						
						
						return true;
					}
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new watch tower
	 **************************************************************************************/
	public boolean createWatchTower(int workers)
	{
		return this.createWatchTower(this.startX, this.startY, workers);
	}
	public boolean createWatchTower(int x, int y, int workers)
	{
		if(WatchTower.PRICE[FOOD] > food || WatchTower.PRICE[METAL] > metal || WatchTower.PRICE[ENERGY] > energy)
			return false;
		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{
					int newID=this.towerIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new tower
						WatchTower tower = new WatchTower(newID, newStructureID, this, workers);
						//add the tower to WatchTower list
						this.watchTowers.addIndex(tower, newID);
						//put the tower on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,tower,true);
						
						
						if(modes.selected == null){
							modes.setSelected();
						}
						
						food -= WatchTower.PRICE[FOOD];
						metal -= WatchTower.PRICE[METAL];
						energy -=WatchTower.PRICE[ENERGY];
						
						return true;
					}
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new fort
	 **************************************************************************************/
	public boolean createFort(int workers)
	{
		return this.createFort(this.startX, this.startY, workers);
	}
	public boolean createFort(int x, int y, int workers)
	{
		if(Fort.PRICE[FOOD] > food || Fort.PRICE[METAL] > metal || Fort.PRICE[ENERGY] > energy)
			return false;
		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{
					int newID=this.fortIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new fort
						Fort fort = new Fort(newID, newStructureID, this, workers);
						//add the fort to Fort list
						this.forts.addIndex(fort, newID);
						//put the fort on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,fort,true);
						
						if(modes.selected == null){
							modes.setSelected();
						}
						
						food -= Fort.PRICE[FOOD];
						metal -= Fort.PRICE[METAL];
						energy -= Fort.PRICE[ENERGY];
						
						return true;
					}
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new power plant
	 **************************************************************************************/
	public boolean createPowerPlant(int workers)
	{
		return this.createPowerPlant(this.startX, this.startY, workers);
	}
	public boolean createPowerPlant(int x, int y, int workers)
	{
		if(PowerPlant.PRICE[FOOD] > food || PowerPlant.PRICE[METAL] > metal || PowerPlant.PRICE[ENERGY] > energy)
			return false;
		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{
					int newID=this.plantIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new plant
						PowerPlant plant = new PowerPlant(newID, newStructureID, this, workers);
						//add the plant to Plant list
						this.plants.addIndex(plant, newID);
						//put the plant on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,plant,true);
						
						if(modes.selected == null){
							modes.setSelected();
						}
						
						food -= PowerPlant.PRICE[FOOD];
						metal -= PowerPlant.PRICE[METAL];
						energy -= PowerPlant.PRICE[ENERGY];
						
						return true;
					}	
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new smelter
	 **************************************************************************************/
	public boolean createSmelter(int workers)
	{
		return this.createSmelter(this.startX, this.startY, workers);
	}
	public boolean createSmelter(int x, int y, int workers)
	{
		if(Smelter.PRICE[FOOD] > food || Smelter.PRICE[METAL] > metal || Smelter.PRICE[ENERGY] > energy)
			return false;

		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{
					int newID=this.smelterIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new plant
						Smelter smelter = new Smelter(newID, newStructureID, this, workers);
						//add the smelter to Smelter list
						this.smelters.addIndex(smelter, newID);
						//put the smelter on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,smelter,true);
						
						if(modes.selected == null){
							modes.setSelected();
						}
						
						food -= Smelter.PRICE[FOOD];
						metal -= Smelter.PRICE[METAL];
						energy -= Smelter.PRICE[ENERGY];
						
						return true;
					}				
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * creates a new laboratory
	 **************************************************************************************/
	public boolean createLaboratory(int wokrers)
	{
		return this.createLaboratory(this.startX, this.startY, workers);
	}
	public boolean createLaboratory(int x, int y, int workers)
	{
		if(Laboratory.PRICE[FOOD] > food || Laboratory.PRICE[METAL] > metal || Laboratory.PRICE[ENERGY] > energy)
			return false;
		
		if(this.gameplayPlayerData.verifyCursor(x, y)) //validate if [x,y] is within bounds
		{
			if(this.gameplayPlayerData.tileIsPassable(x, y) && !this.gameplayPlayerData.tileHasStructure(x, y)) //check if tile has no structures
			{
				int newStructureID=this.structureIDs.getFirstFree(); //get newStructure ID			
				if(newStructureID!=-1)
				{
					int newID=this.labIDs.getFirstFree(); //get new ID
					if(newID!=-1)
					{
						//create a new lab
						Laboratory lab = new Laboratory(newID, newStructureID, this, workers);
						//add the lab to Laboratory list
						this.labs.addIndex(lab, newID);
						//put the lab on the map
						this.gameplayPlayerData.putControllableOnMap(x,y,lab,true);
						if(modes.selected == null){
							modes.setSelected();
						}
						
						food -= Laboratory.PRICE[FOOD];
						metal -= Laboratory.PRICE[METAL];
						energy -= Laboratory.PRICE[ENERGY];
						
						return true;
					}				
					else
					{
						this.structureIDs.release(newStructureID); //release newStructure
					}
				}
			}
		}
		return false;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns selected controllable
	 **************************************************************************************/
	public Controllable getSelected()
	{
		return (Controllable)modes.peekSelected();
	}
	/**************************************************************************************
	 * Author(s): Kyle VanWie
	 * handles prev/next depth: 0 = mode, 1= type, 2 = instance, direction: 0 = prev, 1 = next
	 **************************************************************************************/
	public void selectionCycling(int depth, int direction){
		if(depth == 0){
			if(direction == 0){
				modes.prev();
				modes.setSelected();
			}
			else if(direction == 1){
				modes.next();
				modes.setSelected();
			}
		}
		else if(depth == 1){
			if(!(rally.isEmpty())){
				combatArmy = ((Rally)rally.peekCurrent()).getCombatArmy();
				supportArmy = ((Rally)rally.peekCurrent()).getSupportArmy();
			}
			if(direction == 0){
				((LinkedList<Object>)modes.current.getElement()).prev();
				modes.setSelected();
			}
			else if(direction == 1){
				((LinkedList<Object>)modes.current.getElement()).next();
				modes.setSelected();
			}
		}
		else if(depth == 2){
			if(direction == 0){
				modes.prevSelected();
			}
			else if(direction == 1){
				modes.nextSelected();
			}
		}
	}
	/**************************************************************************************
	 * Author(s): Kyle VanWie
	 * returns a single linkedlist of all units
	 **************************************************************************************/
	public LinkedList<Unit> getAllUnits(){
		LinkedList<Unit> allUnits = new LinkedList<Unit>();
		int sizeOfExplorers = explorers.getSize();
		int sizeOfMelee = melees.getSize();
		int sizeOfRanged = rangeds.getSize();
		int sizeOfColonyShip = colonyShips.getSize();
		for(int i = 0; i < sizeOfExplorers;i++){
			allUnits.addFront(explorers.peekCurrent());
			explorers.next();
		}
		for(int i = 0; i < sizeOfMelee;i++){
			allUnits.addFront(melees.peekCurrent());
			melees.next();
		}
		for(int i = 0; i < sizeOfRanged;i++){
			allUnits.addFront(rangeds.peekCurrent());
			rangeds.next();
		}
		for(int i = 0; i < sizeOfColonyShip;i++){
			allUnits.addFront(colonyShips.peekCurrent());
			colonyShips.next();
		}
		return allUnits;
	}
	/**************************************************************************************
	 * Author(s): Kyle VanWie
	 * returns a single linkedlist of all rallies
	 **************************************************************************************/
	public LinkedList<Rally> getAllRallies(){
		return rally;
	}
	/**************************************************************************************
	 * Author(s): Kyle VanWie
	 * returns a single linkedlist of all structures
	 **************************************************************************************/
	public LinkedList<Structure> getAllStructures(){
		LinkedList<Structure> allStructures = new LinkedList<Structure>();
		int sizeOfBases= bases.getSize();
		int sizeOfFarms= farms.getSize();
		int sizeOfForts= forts.getSize();
		int sizeOfLabs= labs.getSize();
		int sizeOfPlants= plants.getSize();
		int sizeOfSmelters= smelters.getSize();
		int sizeOfWatchTowers= watchTowers.getSize();
		for(int i = 0; i < sizeOfBases;i++){
			allStructures.addFront(bases.peekCurrent());
			bases.next();
		}
		for(int i = 0; i < sizeOfFarms;i++){
			allStructures.addFront(farms.peekCurrent());
			farms.next();
		}
		for(int i = 0; i < sizeOfForts;i++){
			allStructures.addFront(forts.peekCurrent());
			forts.next();
		}
		for(int i = 0; i < sizeOfLabs;i++){
			allStructures.addFront(labs.peekCurrent());
			labs.next();
		}
		for(int i = 0; i < sizeOfPlants;i++){
			allStructures.addFront(plants.peekCurrent());
			plants.next();
		}
		for(int i = 0; i < sizeOfSmelters;i++){
			allStructures.addFront(smelters.peekCurrent());
			smelters.next();
		}
		for(int i = 0; i < sizeOfWatchTowers;i++){
			allStructures.addFront(watchTowers.peekCurrent());
			watchTowers.next();
		}
		
		return allStructures;
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * removes specified explorer
	 **************************************************************************************/
	public void removeExplorer(Explorer e)
	{
		modes.remove(e);
		explorerIDs.release(e.getID());
		unitIDs.release(e.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(e);
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * removes specified colony ship
	 **************************************************************************************/
	public void removeColonyShip(ColonyShip c)
	{
		modes.remove(c);
		colonyShipIDs.release(c.getID());
		unitIDs.release(c.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(c);
		
		playerLose();
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * removes specified melee
	 **************************************************************************************/
	public void removeMelee(Melee m)
	{
		modes.remove(m);
		meleeIDs.release(m.getID());
		unitIDs.release(m.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(m);
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * removes specified melee
	 **************************************************************************************/
	public void removeFarm(Farm farm)
	{
		modes.remove(farm);
		farmIDs.release(farm.getID());
		structureIDs.release(farm.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(farm);
	}
	
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * removes specified ranged
	 **************************************************************************************/
	public void removeRanged(Range r)
	{
		modes.remove(r);
		rangeIDs.release(r.getID());
		unitIDs.release(r.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(r);
	}
	
	/**************************************************************************************
	 * Author(s): ERic Martin-Taylor
	 * removes the base and checks for end game
	 **************************************************************************************/
	public void removeBase(Base base)
	{
		modes.remove(base);
		baseIDs.release(base.getID());
		structureIDs.release(base.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(base);
	
		
		playerLose();
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes fort
	 **************************************************************************************/
	public void removeFort(Fort fort)
	{
		modes.remove(fort);
		fortIDs.release(fort.getID());
		structureIDs.release(fort.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(fort);
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes lab
	 **************************************************************************************/
	public void removeLaboratory(Laboratory lab)
	{
		modes.remove(lab);
		labIDs.release(lab.getID());
		structureIDs.release(lab.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(lab);
	}

	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes powerplant
	 **************************************************************************************/
	public void removePlant(PowerPlant plant)
	{
		modes.remove(plant);
		plantIDs.release(plant.getID());
		structureIDs.release(plant.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(plant);
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes smelter
	 **************************************************************************************/
	public void removeSmelter(Smelter anvil)
	{
		modes.remove(anvil);
		smelterIDs.release(anvil.getID());
		structureIDs.release(anvil.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(anvil);
	}
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes tower
	 **************************************************************************************/
	public void removeTower(WatchTower watcher)
	{
		modes.remove(watcher);
		towerIDs.release(watcher.getID());
		structureIDs.release(watcher.getSupertypeID());
		gameplayPlayerData.removeControllableFromMap(watcher);
	}
	
	
	/**************************************************************************************
	 * Author(s): Eric Martin-Taylor
	 * removes rally
	 **************************************************************************************/
	public void removeRally(Rally group)
	{
		modes.remove(group);
		rallyIDs.release(group.getID());
		gameplayPlayerData.removeControllableFromMap(group);
	}	
	
	/**************************************************************************************
	 * Author(s): Player
	 * Check if the player lost
	 **************************************************************************************/
	public void playerLose(){
		if(this.colonyShips.isEmpty() && this.bases.isEmpty()){
			//TODO stuff all the end game stuff right here
			
			
			System.exit(0);
		}
	}
	/**************************************************************************************
	 * Author(s): Changmin Bae
	 * sets specified resource to val
	 * 0 - food
	 * 1 - metal
	 * 2 - energy
	 **************************************************************************************/
	public void setResource(int resType, int val)
	{
		switch(resType)
		{
		case 0:
			food = val;
			break;
		case 1:
			metal = val;
			break;
		case 2:
			energy = val;
			break;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Pablo Echeverri
	 * adds areaBonus for area around given fort
	 **************************************************************************************/
	public void addFortAreaBonus(Fort fort, int radius)
	{	
		int x=fort.getX();
		int y=fort.getY();
		LinkedList<int[]> list=this.gameplayPlayerData.getArea(x, y, radius);
		this.areaBonusHash.addFort(fort, list);
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns areaBonus for a certain coordinates [x,y]
	 **************************************************************************************/
	public int getAreaBonus(int x, int y)
	{
		return this.areaBonusHash.getBonus(x, y);
	}
	
	/**************************************************************************************
	 * Author(s): Pablo Echeverri
	 *remove fort from table
	 **************************************************************************************/
	public void removeFortAreaBonus(Fort fort)
	{
		this.areaBonusHash.removeFort(fort);
	}
	/**************************************************************************************
	 * Author(s): Pablo Echeverri
	 *get improvement from technology being researched
	 **************************************************************************************/
	public int getTechnology(String type, String name)
	{
		return this.researchTree.getTechnologyImprovement(type, name);
		
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * returns how much of the resources the player gives
	 **************************************************************************************/
	public int[] getAllocation(int metal1, int food1, int energy1, float metal2, float food2, float energy2){
		int [] resources = new int [3];
		
		int temp = Math.min(metal1, (int)(metal2 * this.metal));
		if (temp > metal)
			temp = metal;
		this.metal -= temp;
		resources[METAL] = temp;
		
		temp = Math.min(food1, (int)(food2 * this.food));
		if (temp > food)
			temp = food;
		this.food -= temp;
		resources[FOOD] = temp;
		
		temp = Math.min(energy1, (int)(energy2 * this.energy));
		if (temp > energy)
			temp = energy;
		this.energy -= temp;
		resources[ENERGY] = temp;
		
		return resources;
	}
	
	public int [] getAllocation(int [] base, float [] percent){
		return getAllocation(base[METAL], base[FOOD], base[ENERGY], percent[METAL], percent[FOOD], percent[ENERGY]);
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * increments the amount of workers
	 *************************************************************************************/
	public void incWorker(int amount){this.workers += amount;}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * decrements the workers
	 *************************************************************************************/
	public void decWorker(int amount){this.workers -= amount;}
	/**************************************************************************************
	 * Author(s): Pablo Echeverri
	 *attack unit/structure given the current x,y and the facing direction, and the attacking controllable
	 **************************************************************************************/
	public void attack(int x, int y, int direction, Controllable controllable)
	{
		this.combatManager.attack(x, y, direction, controllable);
	}
	/**************************************************************************************
	 * Author(s): Pablo Echeverri
	 * fort attacks all enemy controllables within given radius
	 **************************************************************************************/
	public void attackArea(int x, int y,Fort fort,int radius)
	{
		this.combatManager.attackArea(x, y, fort, radius);
	}
	
	/**************************************************************************************
	 * Author(s): Eric
	 * return the first base made
	 **************************************************************************************/
	public Base getBase(){ return this.bases.peekFirst();}
}